import { throttle } from 'lodash';
import { BluetoothErrorHandler, createErrorHandler } from './error-handler';
import { createPacketSplitter, PacketSplitter, transferDataFn } from './packet-splitter';
import { BluetoothOptions, ConnectionState, Device, EmitEvent, EventData, EventDataMap } from './types';

export class Bluetooth {
  private _connected: ConnectionState;
  private _deviceList: Device[] = [];
  private _deviceId?: string;
  private _services: UniApp.GetBLEDeviceServicesSuccessData[] = [];
  private _characteristics: UniApp.GetBLEDeviceCharacteristicsSuccessData[] = [];
  private _eventHandlers: { [K in EmitEvent]?: Array<(data: EventData<K>) => void> } = {};
  private _abortController: ReturnType<typeof createAbortController> | undefined;
  private _options: BluetoothOptions;
  public packetSplitter: PacketSplitter;
  public errorHandler: BluetoothErrorHandler;

  constructor(options: BluetoothOptions = {}) {
    this._options = {
      deviceNameFilter: /.*/,
      rssiThreshold: -70,
      mtuSize: 512,
      ...options
    };
    this._deviceList = [];
    this._eventHandlers = {};
    this._deviceId = undefined;
    this._connected = ConnectionState.DISCONNECTED;
    this._abortController = undefined;
    this.errorHandler = this._options.createErrorHandler
      ? this._options.createErrorHandler(this)
      : createErrorHandler(this);
    this.packetSplitter = createPacketSplitter({
      chunkSize: this._options.mtuSize,
      ...(this._options.splitterOptions || {})
    });
  }

  // 初始化蓝牙适配器
  async init(): Promise<void> {
    try {
      await this._checkPlatformFeatures();
      await this._openAdapter();
      this._setupEventListeners();
    } catch (err) {
      this._handleError(err as Error);
    }
  }

  async scanDevices(
    options: { timeout?: number; signal?: AbortSignal } = {}
  ): Promise<typeof this._deviceList | undefined> {
    try {
      const { timeout = 10000, signal } = options;

      this.abortScan();
      this._abortController = createAbortController();
      this._deviceList = [];

      await this._openAdapter();
      await this._startDiscovery();

      return await new Promise<typeof this._deviceList>((resolve, reject) => {
        const timer = setTimeout(() => {
          this._stopDiscovery().then(() => resolve(this._deviceList));
        }, timeout);

        const onDeviceFound = (res: UniNamespace.OnBluetoothDeviceFoundResult) => {
          this._handleDeviceFound(res.devices);
        };

        signal?.addEventListener('abort', () => {
          this._stopDiscovery(timer, onDeviceFound);
          reject(new DOMException('Scan aborted', 'AbortError'));
        });

        const throttleDeviceFound = throttle(onDeviceFound, 500);
        uni.onBluetoothDeviceFound(throttleDeviceFound);
      });
    } catch (err) {
      await this._stopDiscovery();
      this._handleError(err as Error);
    }
  }

  // 连接设备
  async connect(deviceId?: string): Promise<void> {
    try {
      if (!deviceId) {
        throw new Error('无效的设备ID');
      }
      await this._stopDiscovery();
      await uni.createBLEConnection({ deviceId });
      this._deviceId = deviceId;
      this._connected = ConnectionState.CONNECTED;
      await this._discoverServices();
    } catch (err) {
      this._handleError(err as Error);
    }
  }

  // 发现蓝牙特征值
  async discoverCharacteristics(serviceId: string) {
    if (!this._deviceId) {
      throw new Error('设备未连接，请先建立连接');
    }
    if (!serviceId?.trim()) {
      throw new Error('无效的服务ID');
    }
    try {
      const { characteristics } = await uni.getBLEDeviceCharacteristics({
        deviceId: this._deviceId,
        serviceId
      });
      this._characteristics = characteristics;
      return characteristics;
    } catch (err) {
      this._handleError(err as Error);
      return undefined;
    }
  }

  // 启用通知
  async startNotification(serviceId: string, characteristicId: string): Promise<void> {
    try {
      if (!this._deviceId) {
        throw new Error('设备未连接，请先建立连接');
      }
      await uni.notifyBLECharacteristicValueChange({
        deviceId: this._deviceId,
        serviceId,
        characteristicId,
        state: true
      });
    } catch (error) {
      this._handleError(error as Error);
    }
  }

  // 断开连接
  async disconnect(): Promise<void> {
    try {
      if (this._connected === ConnectionState.DISCONNECTED || !this._deviceId) return;
      await uni.closeBLEConnection({ deviceId: this._deviceId });
      this._connected = ConnectionState.DISCONNECTED;
      this._deviceId = undefined;
    } catch (error) {
      this._handleError(error as Error);
    }
  }

  // 写入数据
  async write(
    serviceId: string,
    characteristicId: string,
    value: ArrayBuffer,
    options: { retry?: number } = {}
  ): Promise<void> {
    const { retry = 2 } = options;
    let attempts = 0;
    while (attempts <= retry) {
      try {
        if (!this._deviceId) {
          throw new Error('设备未连接，请先建立连接');
        }
        await uni.writeBLECharacteristicValue({
          deviceId: this._deviceId,
          serviceId,
          characteristicId,
          value: Array.from(new Uint8Array(value))
        });
        return;
      } catch (err) {
        if (attempts++ >= retry) {
          this._handleError(err as Error);
        } else {
          await new Promise((resolve) => setTimeout(resolve, 300));
        }
      }
    }
  }

  async send(data: Uint8Array, fn: transferDataFn, packetSequenceStart = 1): Promise<void> {
    if (!this._deviceId) {
      throw new Error('设备未连接，请先建立连接');
    }
    return this.packetSplitter.send(data, fn, packetSequenceStart);
  }

  async setMTU(size = 512): Promise<void> {
    try {
      await uni.setBLEMTU({
        deviceId: this._deviceId!,
        mtu: size
      });
    } catch (error) {
      this._handleError(error as Error);
    }
  }

  // 手动取消扫描
  abortScan(): void {
    if (this._abortController) {
      this._abortController.abort();
      this._abortController = undefined;
    }
  }

  // 获取当前状态
  getState() {
    return {
      deviceId: this._deviceId,
      connected: this._connected,
      services: [...this._services],
      characteristics: [...this._characteristics],
      devices: [...this._deviceList]
    };
  }

  // 事件监听
  on<K extends EmitEvent>(event: K, listener: (data: EventDataMap[K]) => void): this {
    if (!this._eventHandlers[event]) {
      this._eventHandlers[event] = [];
    }
    (this._eventHandlers[event] as Array<(data: EventDataMap[K]) => void>).push(listener);
    return this;
  }

  once<K extends EmitEvent>(event: K, listener: (data: EventDataMap[K]) => void): this {
    const onceListener = (data: EventDataMap[K]) => {
      listener(data);
      this.off(event, onceListener);
    };
    return this.on(event, onceListener);
  }

  // 移除监听
  off<K extends EmitEvent>(event: K, listener: (data: EventDataMap[K]) => void): this {
    const handlers = this._eventHandlers[event] as Array<(data: EventDataMap[K]) => void> | undefined;
    if (handlers) {
      (this._eventHandlers[event] as any) = handlers.filter((h) => h !== listener);
    }
    return this;
  }

  // 私有方法
  private async _checkPlatformFeatures(): Promise<void> {
    if (typeof uni.openBluetoothAdapter !== 'function') {
      throw new Error('当前环境不支持蓝牙功能');
    }
  }

  private async _openAdapter(): Promise<boolean> {
    const getAdapterState = async (): Promise<boolean> => {
      try {
        const res = await uni.getBluetoothAdapterState();
        return res.available;
      } catch (err) {
        return false;
      }
    };

    try {
      let available = await getAdapterState();
      if (available) return true;

      await uni.openBluetoothAdapter();
      return await getAdapterState();
    } catch (error) {
      this._handleError(error as Error);
      return false;
    }
  }

  private _setupEventListeners(): void {
    uni.onBLEConnectionStateChange((res) => {
      this._connected = res.connected ? ConnectionState.CONNECTED : ConnectionState.DISCONNECTED;
      this._emit(EmitEvent.connectionStateChange, res);
    });

    uni.onBLECharacteristicValueChange((res) => {
      this._emit(EmitEvent.data, {
        deviceId: res.deviceId,
        serviceId: res.serviceId,
        characteristicId: res.characteristicId,
        value: res.value as unknown as ArrayBuffer
      });
    });

    uni.onBluetoothAdapterStateChange((res) => {
      this._emit(EmitEvent.adapterStateChange, {
        available: res.available,
        discovering: res.discovering
      });
    });
  }

  private async _startDiscovery(): Promise<void> {
    try {
      await uni.startBluetoothDevicesDiscovery();
    } catch (error) {
      this._handleError(error as Error);
    }
  }

  private async _stopDiscovery(timer?: NodeJS.Timeout, listener?: any): Promise<void> {
    clearTimeout(timer);
    if (listener) uni.offBluetoothDeviceFound(listener);
    try {
      await uni.stopBluetoothDevicesDiscovery();
      await new Promise((resolve) => setTimeout(resolve, 100));
    } catch (err) {
      console.warn('停止扫描失败:', err);
    }
  }

  private _handleDeviceFound(devices: Device[]): void {
    const validDevices = devices.filter(
      (device) =>
        device.name?.match(this._options.deviceNameFilter!) &&
        (device.RSSI ?? -Infinity) > (this._options.rssiThreshold ?? -70)
    );

    validDevices.forEach((newDevice) => {
      const exists = this._deviceList.some((d) => d.deviceId === newDevice.deviceId);
      if (!exists) {
        this._deviceList.push(newDevice);
        this._emit(EmitEvent.deviceFound, newDevice);
      }
    });
  }

  private async _discoverServices(): Promise<void> {
    if (!this._deviceId) return;

    const { services } = await uni.getBLEDeviceServices({ deviceId: this._deviceId });
    this._services = services;
  }

  private _emit<K extends EmitEvent>(event: K, data: EventDataMap[K]): void {
    const handlers = this._eventHandlers[event] as Array<(data: EventDataMap[K]) => void> | undefined;
    handlers?.forEach((handler) => handler(data));
  }

  private _handleError(err: any): void {
    const errorMap: Record<number, string> = {
      10000: '未初始化蓝牙适配器',
      10001: '当前蓝牙适配器不可用',
      10002: '没有找到指定设备',
      10003: '连接失败',
      10004: '没有找到指定服务',
      10005: '没有找到指定特征值',
      10006: '当前连接已断开',
      10007: '当前特征值不支持此操作',
      10008: '其余所有系统上报的异常',
      10009: '系统特有错误：iOS特有异常',
      10010: '连接超时'
    };

    if ('errCode' in err) {
      (err as any).errMsg = errorMap[err.errCode] || err.errMsg;
    }

    this.errorHandler?.handle(err);
  }
}

// 兼容实现
function createAbortController(): { signal: { aborted: boolean }; abort: () => void; onabort: null } {
  if (typeof AbortController !== 'undefined') {
    return new AbortController() as unknown as ReturnType<typeof createAbortController>;
  }

  return {
    signal: { aborted: false },
    abort() {
      this.signal.aborted = true;
      if (this.onabort) (this.onabort as () => void)();
    },
    onabort: null
  };
}
